package com.zxl.always.on.display.ui.widget.clock.analog;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.TypedArray;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.View;

import com.zxl.always.on.display.ApplicationHelper;
import com.zxl.always.on.display.R;
import com.zxl.always.on.display.model.PaletteSet;
import com.zxl.always.on.display.model.bean.PaletteItem;
import com.zxl.always.on.display.tools.GraphicUtils;
import com.zxl.always.on.display.tools.ResourceUtils;
import com.zxl.always.on.display.tools.Size;
import com.zxl.always.on.display.ui.widget.clock.StandAloneClock;

import java.util.Calendar;
import java.util.HashMap;
import java.util.TimeZone;

public class AnalogClockDrawingView extends View implements StandAloneClock {

    private int mBgSize;

    private boolean mTickerStopped = false;

    private boolean mIsStandAloneMode;

    private Calendar mCalendar = Calendar.getInstance();

    private int mAvailableWidth, mAvailableHeight;

    private float mHourDegree, mMinDegree, mSecDegree;

    private PaletteItem paletteItem;

    private Runnable mTicker = new Runnable() {
        public void run() {
            if (mTickerStopped) {
                return;
            }
            mCalendar.setTimeInMillis(System.currentTimeMillis());
            mSecDegree = 6.0F * (mCalendar.get(Calendar.SECOND) + mCalendar.get(Calendar.MILLISECOND) / 1000.0F);
            invalidate();
            long l1 = SystemClock.uptimeMillis();
            long l2 = l1 + (1000L - l1 % 1000L);
            ApplicationHelper.getHandler().postAtTime(AnalogClockDrawingView.this.mTicker, l2);
        }
    };

    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {
                String str = intent.getStringExtra("time-zone");
                mCalendar.setTimeZone(TimeZone.getTimeZone(str));
            }
            refreshTime();
            if (null != mOnTimerUpdateListener) {
                mOnTimerUpdateListener.onUpdate();
            }
        }
    };

    private HashMap<ComponentType, AnalogClockComponentInfo> mClockComponentInfoMap = new HashMap();

    private ComponentType[] mComponentType = new ComponentType[8];

    private OnTimerUpdateListener mOnTimerUpdateListener;

    public AnalogClockDrawingView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.AnalogClockDrawingView);
        mBgSize = ResourceUtils.getDimensionPixelOffset(typedArray, R.styleable.AnalogClockDrawingView_bg_size, -1);

        mComponentType[0] = ComponentType.CLOCK_FACE;
        mComponentType[1] = ComponentType.CLOCK_FACE_OVERLAY;
        mComponentType[2] = ComponentType.HOUR_HAND;
        mComponentType[3] = ComponentType.HOUR_HAND_OVERLAY;
        mComponentType[4] = ComponentType.MINUTE_HAND;
        mComponentType[5] = ComponentType.MINUTE_HAND_OVERLAY;
        mComponentType[6] = ComponentType.SECOND_HAND;
        mComponentType[7] = ComponentType.PIN_HAND;

        for (ComponentType localComponentType : mComponentType) {
            this.mClockComponentInfoMap.put(localComponentType, new AnalogClockComponentInfo(localComponentType, typedArray));
        }
        typedArray.recycle();

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mAvailableHeight = getMeasuredHeight();
        mAvailableWidth = getMeasuredWidth();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int i = this.mAvailableWidth / 2;
        int j = this.mAvailableHeight / 2;

        Drawable background = getBackground();
        if (background != null) {
            setBoundAtCenter(background, mBgSize, mBgSize);
        }
        for (ComponentType componentType : mComponentType) {
            AnalogClockComponentInfo info = mClockComponentInfoMap.get(componentType);
            if (null != info && info.mDrawable == null) {
                continue;
            }
            float mRotation = 0;
            if (componentType == ComponentType.HOUR_HAND || componentType == ComponentType.HOUR_HAND_OVERLAY) {
                mRotation = mHourDegree;
            } else if (componentType == ComponentType.MINUTE_HAND || componentType == ComponentType.MINUTE_HAND_OVERLAY) {
                mRotation = mMinDegree;
            } else if (componentType == ComponentType.SECOND_HAND) {
                mRotation = mSecDegree;
            }
            drawClockComponent(canvas, info.getDrawable(), info.getWidth(), info.getHeight(), info.getShadowX(), info.getShadowY(), i, j, mRotation);
        }
    }

    public void setOnTimerUpdateListener(OnTimerUpdateListener mOnTimerUpdateListener) {
        this.mOnTimerUpdateListener = mOnTimerUpdateListener;
    }

    @Override
    public void setPaletteColor(int position) {
        paletteItem = PaletteSet.getPaletteItems().get(position);
        invalidate();
    }

    @Override
    public void setBackgroundStyle(int position) {
    }

    private void drawClockComponent(Canvas canvas, Drawable drawable, int width, int height, int dx, int dy, int px, int py, float degrees) {
        canvas.save();
        setBoundAtCenter(drawable, width, height);
        canvas.translate(dx, dy);
        canvas.rotate(degrees, px, py);
        if (null != paletteItem) {
            if (paletteItem.getType() == PaletteItem.Type.SOLID) {
                drawable.setColorFilter(paletteItem.getColor(), PorterDuff.Mode.SRC_IN);
            } else {
                Rect bounds = drawable.getBounds();
                LinearGradient linearGradient = new LinearGradient(bounds.left, bounds.top, bounds.right, bounds.bottom, paletteItem.getColors(), paletteItem.getPositions(), Shader.TileMode.CLAMP);
                Drawable drawable1 = GraphicUtils.getLinearGradientDrawable(getContext(), drawable, width, height, -30, linearGradient);
                setBoundAtCenter(drawable1, width, height);
                drawable1.draw(canvas);
                canvas.restore();
                return;
            }
        } else {
            drawable.setColorFilter(null);
        }
        drawable.draw(canvas);
        canvas.restore();
    }

    private void setBoundAtCenter(Drawable drawable, int width, int height) {
        if (drawable != null) {
            int left = (this.mAvailableWidth - width) / 2;
            int top = (this.mAvailableHeight - height) / 2;
            drawable.setBounds(left, top, left + width, top + height);
        }
    }

    private void setTime(int hour, int min, int sec) {
        float f1 = min / 60.0F;
        float f2 = f1 + hour;
        this.mSecDegree = (6.0F * (sec + mCalendar.get(Calendar.MILLISECOND) / 1000.0F));
        this.mMinDegree = (f1 * 360.0F);
        this.mHourDegree = (360.0F * (f2 / 12.0F));
    }

    @Override
    public void pauseStandAloneMode() {
        setStandAloneMode(false);
    }

    @Override
    public void refreshTime() {
        mCalendar.setTimeInMillis(System.currentTimeMillis());
        setTime(mCalendar.get(Calendar.HOUR), mCalendar.get(Calendar.MINUTE), mCalendar.get(Calendar.MINUTE));
        invalidate();
    }

    @Override
    public void resumeStandAloneMode() {
        setStandAloneMode(mIsStandAloneMode);
    }

    @Override
    public void setStandAloneMode(boolean isStandAlone) {
        mIsStandAloneMode = isStandAlone;
        if (mIsStandAloneMode) {
            mTickerStopped = false;
            if (mClockComponentInfoMap.get(ComponentType.SECOND_HAND).getDrawable() != null) {
                mTicker.run();
            }
            IntentFilter localIntentFilter = new IntentFilter();
            localIntentFilter.addAction(Intent.ACTION_TIME_TICK);
            localIntentFilter.addAction(Intent.ACTION_TIME_CHANGED);
            localIntentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
            try {
                getContext().registerReceiver(mReceiver, localIntentFilter);
            } catch (Exception e) {
                e.printStackTrace();
            }
            refreshTime();
        } else {
            mTickerStopped = true;
            ApplicationHelper.getHandler().removeCallbacks(mTicker);
            try {
                getContext().unregisterReceiver(mReceiver);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public enum ComponentType {

        CLOCK_FACE_OVERLAY(R.styleable.AnalogClockDrawingView_clock_face_overlay_src, R.styleable.AnalogClockDrawingView_clock_face_size),

        CLOCK_FACE(R.styleable.AnalogClockDrawingView_clock_face_src, R.styleable.AnalogClockDrawingView_clock_face_size),

        HOUR_HAND_OVERLAY(R.styleable.AnalogClockDrawingView_hour_hand_overlay_src, R.styleable.AnalogClockDrawingView_hour_hand_width, R.styleable.AnalogClockDrawingView_hour_hand_height,
                R.styleable.AnalogClockDrawingView_hour_hand_shadow_x, R.styleable.AnalogClockDrawingView_hour_hand_shadow_y, R.styleable.AnalogClockDrawingView_hour_hand_glow_enabled),

        HOUR_HAND(R.styleable.AnalogClockDrawingView_hour_hand_src, R.styleable.AnalogClockDrawingView_hour_hand_width, R.styleable.AnalogClockDrawingView_hour_hand_height,
                R.styleable.AnalogClockDrawingView_hour_hand_shadow_x, R.styleable.AnalogClockDrawingView_hour_hand_shadow_y, R.styleable.AnalogClockDrawingView_hour_hand_glow_enabled),

        MINUTE_HAND_OVERLAY(R.styleable.AnalogClockDrawingView_minute_hand_overlay_src, R.styleable.AnalogClockDrawingView_minute_hand_width, R.styleable.AnalogClockDrawingView_minute_hand_height,
                R.styleable.AnalogClockDrawingView_minute_hand_shadow_x, R.styleable.AnalogClockDrawingView_minute_hand_shadow_y, R.styleable.AnalogClockDrawingView_minute_hand_glow_enabled),

        MINUTE_HAND(R.styleable.AnalogClockDrawingView_minute_hand_src, R.styleable.AnalogClockDrawingView_minute_hand_width, R.styleable.AnalogClockDrawingView_minute_hand_height,
                R.styleable.AnalogClockDrawingView_minute_hand_shadow_x, R.styleable.AnalogClockDrawingView_minute_hand_shadow_y, R.styleable.AnalogClockDrawingView_minute_hand_glow_enabled),

        SECOND_HAND(R.styleable.AnalogClockDrawingView_second_hand_src, R.styleable.AnalogClockDrawingView_second_hand_width, R.styleable.AnalogClockDrawingView_second_hand_height),

        PIN_HAND(R.styleable.AnalogClockDrawingView_pin_head_src, R.styleable.AnalogClockDrawingView_pin_head_size, R.styleable.AnalogClockDrawingView_pin_head_size,
                R.styleable.AnalogClockDrawingView_pin_head_shadow_x, R.styleable.AnalogClockDrawingView_pin_head_shadow_y, R.styleable.AnalogClockDrawingView_pin_head_shadow_y);

        int mDrawableIndex = -1;
        int mGlowEnabledIndex = -1;
        int mHeightIndex = -1;
        boolean mIsShadowEnabled = false;
        int mShadowXIndex = -1;
        int mShadowYIndex = -1;
        int mWidthIndex = -1;

        ComponentType(int drawable, int size) {
            this.mDrawableIndex = drawable;
            this.mWidthIndex = size;
            this.mHeightIndex = size;
        }

        ComponentType(int drawable, int width, int height) {
            this.mDrawableIndex = drawable;
            this.mWidthIndex = width;
            this.mHeightIndex = height;
        }

        ComponentType(int drawable, int width, int height, int shadowX, int shadowY, int glowEnable) {
            this.mDrawableIndex = drawable;
            this.mWidthIndex = width;
            this.mHeightIndex = height;
            this.mShadowXIndex = shadowX;
            this.mShadowYIndex = shadowY;
            this.mGlowEnabledIndex = glowEnable;
            if ((shadowX != -1) && (shadowY != -1)) {
                this.mIsShadowEnabled = true;
            }
        }
    }

    public class AnalogClockComponentInfo {

        Drawable mDrawable;
        BitmapDrawable mGlowedDrawable;
        int mHeight;
        boolean mIsEnable = true;
        boolean mIsGlowEnabled = false;
        float mRotation;
        BitmapDrawable mShadowDrawable;
        int mShadowX;
        int mShadowY;
        final AnalogClockDrawingView.ComponentType mType;
        int mWidth;

        public AnalogClockComponentInfo(AnalogClockDrawingView.ComponentType componentType, TypedArray typedArray) {
            this.mType = componentType;
            if (-1 != componentType.mDrawableIndex) {
                this.mDrawable = typedArray.getDrawable(componentType.mDrawableIndex);
            }
            if (-1 != componentType.mWidthIndex) {
                this.mWidth = ResourceUtils.getDimensionPixelOffset(typedArray, componentType.mWidthIndex, -1);
            }
            if (-1 != componentType.mHeightIndex) {
                this.mHeight = ResourceUtils.getDimensionPixelOffset(typedArray, componentType.mHeightIndex, -1);
            }
            if (-1 != componentType.mShadowXIndex) {
                this.mShadowX = ResourceUtils.getDimensionPixelOffset(typedArray, componentType.mShadowXIndex, -1);
            }
            if (-1 != componentType.mShadowYIndex) {
                this.mShadowY = ResourceUtils.getDimensionPixelOffset(typedArray, componentType.mShadowYIndex, -1);
            }
            if (-1 != componentType.mGlowEnabledIndex) {
                this.mIsGlowEnabled = typedArray.getBoolean(componentType.mGlowEnabledIndex, true);
            }
        }

        public Drawable getDrawable() {
            if (this.mIsGlowEnabled) {
                if (this.mGlowedDrawable == null) {
                    this.mGlowedDrawable = GraphicUtils.getGlowDrawable(getContext(), this.mDrawable, this.mWidth, this.mHeight, BlurMaskFilter.Blur.NORMAL, 4, 1073741824, 40, false);
                }
                return this.mGlowedDrawable;
            }
            return this.mDrawable;
        }

        public Size getDrawableSize() {
            if (getDrawable() != null) {
                if (this.mGlowedDrawable != null) {
                    return new Size(this.mGlowedDrawable.getIntrinsicWidth(), this.mGlowedDrawable.getIntrinsicHeight());
                }
                return new Size(this.mWidth, this.mHeight);
            }
            return new Size(0, 0);
        }

        public BitmapDrawable getGlowedDrawable() {
            return this.mGlowedDrawable;
        }

        public int getHeight() {
            return this.mHeight;
        }

        public float getRotation() {
            return this.mRotation;
        }

        public BitmapDrawable getShadowDrawable() {
            if (this.mType.mIsShadowEnabled) {
                if (this.mShadowDrawable == null) {
                    Rect localRect = this.mDrawable.copyBounds();
                    this.mDrawable.setBounds(0, 0, this.mWidth, this.mHeight);
                    BitmapDrawable localBitmapDrawable = GraphicUtils.getGlowDrawable(getContext(), this.mDrawable, this.mWidth, this.mHeight, BlurMaskFilter.Blur.NORMAL, 4, 1073741824, 40, true);
                    if (localBitmapDrawable != null) {
                        setBoundAtCenter(localBitmapDrawable, localBitmapDrawable.getIntrinsicWidth(), localBitmapDrawable.getIntrinsicHeight());
                        this.mShadowDrawable = localBitmapDrawable;
                        this.mDrawable.setBounds(localRect);
                    }
                }
                return this.mShadowDrawable;
            }
            return null;
        }

        public Size getShadowDrawableSize() {
            BitmapDrawable localBitmapDrawable = getShadowDrawable();
            if (localBitmapDrawable != null) {
                return new Size(localBitmapDrawable.getIntrinsicWidth(), localBitmapDrawable.getIntrinsicHeight());
            }
            return new Size(0, 0);
        }

        public int getShadowX() {
            return this.mShadowX;
        }

        public int getShadowY() {
            return this.mShadowY;
        }

        public AnalogClockDrawingView.ComponentType getType() {
            return this.mType;
        }

        public int getWidth() {
            return this.mWidth;
        }

        public boolean isEnable() {
            return this.mIsEnable;
        }

        public boolean isGlowEnabled() {
            return this.mIsGlowEnabled;
        }

        public void setDrawable(Drawable paramDrawable) {
            this.mDrawable = paramDrawable;
        }

        public void setEnable(boolean paramBoolean) {
            this.mIsEnable = paramBoolean;
        }

        public void setGlowEnabled(boolean paramBoolean) {
            this.mIsGlowEnabled = paramBoolean;
        }

        public void setGlowedDrawable(BitmapDrawable paramBitmapDrawable) {
            this.mGlowedDrawable = paramBitmapDrawable;
        }

        public void setHeight(int paramInt) {
            this.mHeight = paramInt;
        }

        public void setRotation(float paramFloat) {
            this.mRotation = paramFloat;
        }

        public void setShadowDrawable(BitmapDrawable paramBitmapDrawable) {
            this.mShadowDrawable = paramBitmapDrawable;
        }

        public void setShadowX(int paramInt) {
            this.mShadowX = paramInt;
        }

        public void setShadowY(int paramInt) {
            this.mShadowY = paramInt;
        }

        public void setWidth(int paramInt) {
            this.mWidth = paramInt;
        }

    }


    public interface OnTimerUpdateListener {

        void onUpdate();

    }

}
